#! /usr/bin/env python3
# -*- encoding:utf-8 -*-

import logging
import sql
from page import Page
logging.basicConfig(level=logging.INFO)


class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):  # 类似构造方法
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        tableName = attrs.get("__table__", None) or name
        logging.info("find model: %s (table: %s)" % (name, tableName))
        mapping = dict()
        fields = []
        primaryKey = None
        for k, v in attrs.items():
            if (isinstance(v, Field)):
                logging.info("find mapping: %s  ===>  %s" % (k, v))
                mapping[k] = v
                if v.primary_key:
                    if primaryKey:
                        raise RuntimeError("重复主键: %s" % k)
                    primaryKey = k
                else:
                    fields.append(k)
        if not primaryKey:
            raise RuntimeError("主键缺失")
        for k in mapping:
            attrs.pop(k)
        escaped_field = list(map(lambda f: '`%s`' % f, fields))
        attrs['__mappings__'] = mapping
        attrs['__table__'] = tableName
        attrs['__primary_key__'] = primaryKey
        attrs['__field__'] = fields
        attrs['__select__'] = 'select `%s`, %s from `%s`' % (
            primaryKey, ', '.join(escaped_field), tableName)
        attrs['__insertwithkey__'] = 'insert into `%s` (%s, `%s`) values (%s) \
            ' % (tableName, ', '.join(escaped_field), primaryKey,
                 create_args_string(len(escaped_field) + 1))
        attrs['__insert__'] = 'insert into `%s` (%s) values (%s)' % (
            tableName, ', '.join(escaped_field),
            create_args_string(len(escaped_field)))
        attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (
            tableName, ', '.join(
                map(lambda f: '`%s`=?' % (mapping.get(f).name or f), fields)),
            primaryKey)
        attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName,
                                                                 primaryKey)
        return type.__new__(cls, name, bases, attrs)


def create_args_string(length):
    argsList = ['?' for x in range(length)]
    return ','.join(argsList)


class Field(object):
    def __init__(self, name, column_type, primary_key, default):
        self.name = name
        self.column_type = column_type
        self.primary_key = primary_key
        self.default = default

    def __str__(self):
        return '<%s, %s:%s>' % (self.__class__.__name__, self.column_type,
                                self.name)


class StringField(Field):
    def __init__(self,
                 name=None,
                 column_type='varchar(100)',
                 primary_key=False,
                 default=None):
        super().__init__(name, column_type, primary_key, default)


class IntegerField(Field):
    def __init__(self,
                 name=None,
                 column_type='int(11)',
                 primary_key=False,
                 default=None):
        super().__init__(name, column_type, primary_key, default)


class BooleanField(Field):
    def __init__(self,
                 name=None,
                 column_type='tinyint(1)',
                 primary_key=False,
                 default=None):
        super().__init__(name, column_type, primary_key, default)


class FloatField(Field):
    def __init__(self,
                 name=None,
                 column_type='bigint(20)',
                 primary_key=False,
                 default=None):
        super().__init__(name, column_type, primary_key, default)


class TextField(Field):
    def __init__(self,
                 name=None,
                 column_type='text',
                 primary_key=False,
                 default=None):
        super().__init__(name, column_type, primary_key, default)


class Model(dict, metaclass=ModelMetaclass):
    def __init__(self, **kw):
        super(Model, self).__init__(kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def getValue(self, key):
        return getattr(self, key, None)

    def getValueOrDefault(self, key):
        value = getattr(self, key, None)
        if value is None:
            filed = self.__mappings__[key]
            if filed.default is not None:
                value = filed.default() if callable(
                    filed.default) else filed.default
                logging.info("using default value for %s:%s" % (key,
                                                                str(value)))
                setattr(self, key, value)
        return value

    async def save(self):
        args = list(map(self.getValueOrDefault, self.__field__))
        # args.append(self.getValueOrDefault(self.__primary_key__))
        rows = await sql.execute(self.__insert__, args)
        if rows != 1:
            logging.warn("插入失败，影响行数：%s" % rows)

    async def saveWithkey(self):
        args = list(map(self.getValueOrDefault, self.__field__))
        args.append(self.getValueOrDefault(self.__primary_key__))
        rows = await sql.execute(self.__insertwithkey__, args)
        if rows != 1:
            logging.warn("插入失败，影响行数：%s" % rows)

    @classmethod
    async def findOne(cls, pk):
        '根据主键查询数据'
        rs = await sql.select('%s where %s = ?' %
                              (cls.__select__, cls.__primary_key__), [pk], 1)
        if len(rs) == 0:
            return None
        return cls(**rs[0])

    async def findCount(self):
        '查询数量'
        queryList = []
        queryargs = []
        querystr = ''
        for k in self.__field__:
            if self.getValue(k) is not None:
                queryList.append('`%s`=?' % k)
                queryargs.append(self.getValue(k))
        if len(queryList) > 0:
            querystr = 'where %s ' % ' and '.join(queryList)
        rs = await sql.select(
            'select ifnull(count(1),0) count from ( %s %s ) t' %
            (self.__select__, querystr), queryargs, 1)
        return int(rs[0].get('count'))

    async def findPage(self, pagebean):
        if not isinstance(pagebean, Page):
            raise AttributeError("分页错误")
        '分页查询'
        queryList = []
        queryargs = []
        querystr = ''
        for k in self.__field__:
            if self.getValue(k) is not None:
                queryList.append('`%s`=?' % k)
                queryargs.append(self.getValue(k))
        if len(queryList) > 0:
            querystr = 'where %s ' % ' and '.join(queryList)
        rs = await sql.select(
            '%s %s' % (self.__select__, querystr), queryargs, page=pagebean)
        pagebean.trans2Page(await self.findCount(), rs)
        return pagebean
